package com.suray.wcs.service.plc;

import com.suray.basic.wcs.path.core.Node;
import com.suray.basic.wcs.path.core.Route;
import com.suray.basic.wcs.path.core.api.RouteFactory;
import com.suray.basic.wcs.path.core.api.impl.RouteFactoryImp;
import com.suray.basic.wcs.path.enums.NodeStatus;
import com.suray.basic.wcs.plc.core.Plc;
import com.suray.basic.wcs.plc.pojo.oper.lifter.LifterOper;
import com.suray.basic.wcs.plc.utils.PlcUnit;
import com.suray.basic.wcs.utils.Coord;
import com.suray.commin.core.utils.SpringContextHolder;
import com.suray.wcs.service.core.util.LockNodeManager;
import com.suray.wcs.service.system.entity.TaskDB;
import com.suray.wcs.service.system.service.impl.NodeDBServiceImpl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 根据路径规划查找提升机路径节点
 */
public class LifterNodeAnalyze {

    //提升机路径节点
    private Map<LifterPathNode, Node> lifterPathNodeMap = new HashMap<>();
    //单前路径上的提升机
    private LifterOper lifterOper;
    //提升机路径节点规划
    private List<Node> lifterNodes = new ArrayList<>();
    //路径(该路径不包括提升机路径)
    private RouteFactory routeFactory;
    //任务
    private TaskDB taskDB;

    public Map<LifterPathNode, Node> getLifterPathNodeMap() {
        return lifterPathNodeMap;
    }

    //节点
    private NodeDBServiceImpl nodeDBService = SpringContextHolder.getBean(NodeDBServiceImpl.class);

    public LifterOper getLifterOper() {
        return lifterOper;
    }

    public LifterNodeAnalyze(List<Route> toUnLoad, TaskDB taskDB) {
        this.taskDB = taskDB;
        this.routeFactory = new RouteFactoryImp(nodeDBService.getNodeList(), LockNodeManager.getLockedNodes(),
                null, 0);
        initLifterPathNode(toUnLoad);
    }

    /**
     * 找到当前路径上经过提升机时提升机起点、提升机点、提升机终点
     * 路径规划找提升机路径节点
     *
     * @param toUnLoad
     */
    private void initLifterPathNode(List<Route> toUnLoad) {
        //起始层路径
        Route startRoute = toUnLoad.get(0);
        //提升机点位
        Node lifterNode = startRoute.getEndGrid();
        List<Node> startNodeList = startRoute.getNodeList();
        //提升机起始点位
        Node lifterStartNode = startNodeList.get(startNodeList.size() - 2);
        //终点层路径
        Route endRoute = toUnLoad.get(toUnLoad.size() - 1);
        List<Node> endNodeList = endRoute.getNodeList();
        Node lifterEndNode = endNodeList.get(1);
        lifterPathNodeMap.put(LifterPathNode.START_NODE, lifterStartNode);
        lifterPathNodeMap.put(LifterPathNode.LIFTER_PATH_NODE, lifterNode);
        lifterPathNodeMap.put(LifterPathNode.END_NODE, lifterEndNode);
        initLifterNodes(lifterPathNodeMap.get(LifterPathNode.LIFTER_PATH_NODE),
                lifterPathNodeMap.get(LifterPathNode.START_NODE), lifterPathNodeMap.get(LifterPathNode.END_NODE));
        lifterOper = PlcUnit.getLifterOper(lifterPathNodeMap.get(LifterPathNode.LIFTER_PATH_NODE));
        findBestLifterPath(lifterPathNodeMap);
    }

    /**
     * 如果路径规划找到的节点被锁或者有货,如果提升机另外节点没货且没被锁
     * 找到另外节点
     *
     * @param lifterPathNodeMap
     */
    private void findBestLifterPath(Map<LifterPathNode, Node> lifterPathNodeMap) {
        Node lifterStartNode = lifterPathNodeMap.get(LifterPathNode.START_NODE);
        Node lifterEndNode = lifterPathNodeMap.get(LifterPathNode.END_NODE);
        if (lifterStartNode.getNodeStatus() == NodeStatus.Y || LockNodeManager.checkNodesLocked(lifterStartNode)) {
            Node bestNode = findBestNode(lifterStartNode);
            bestNode.setZ(lifterStartNode.getZ());
            if (isCanGo(bestNode) && bestNode.getNodeStatus() == NodeStatus.N) {
                lifterPathNodeMap.put(LifterPathNode.START_NODE, bestNode);
            }
        }
        if (lifterEndNode.getNodeStatus() == NodeStatus.Y || LockNodeManager.checkNodesLocked(lifterEndNode)) {
            Node bestNode = findBestNode(lifterEndNode);
            bestNode.setZ(lifterEndNode.getZ());
            if (isCanGo(bestNode) && bestNode.getNodeStatus() == NodeStatus.N) {
                lifterPathNodeMap.put(LifterPathNode.END_NODE, bestNode);
            }
        }
    }

    /**
     * @param lifterNode
     * @param lifterStartNode
     * @param lifterEndNode
     */
    private void initLifterNodes(Node lifterNode, Node lifterStartNode, Node lifterEndNode) {
        List<Node> lifterConnects = findLifterConnect(lifterNode);
        if (lifterConnects.size() == 3) {
            //先找起点
            for (int i = 0; i < 2; i++) {
                if (!(lifterConnects.get(i).getX() == lifterStartNode.getX() && lifterConnects.get(i).getY() == lifterStartNode.getY())) {
                    Node node = lifterConnects.get(i);
                    lifterNodes.add(new Node(node.getX(), node.getY(), lifterStartNode.getZ()));
                }
            }
            //再找终点
            for (int i = 0; i < 2; i++) {
                if (!(lifterConnects.get(i).getX() == lifterEndNode.getX() && lifterConnects.get(i).getY() == lifterEndNode.getY())) {
                    Node node = lifterConnects.get(i);
                    lifterNodes.add(new Node(node.getX(), node.getY(), lifterEndNode.getZ()));
                }
            }
            lifterNodes.add(lifterConnects.get(2));
        }
    }

    private List<Node> findLifterConnect(Node lifterNode) {
        List<Node> list = new ArrayList<>();
        Coord coord = Plc.deviceLifterCoordMap.get(new Coord(lifterNode.getX(), lifterNode.getY(), 1)).getCoord();
        if (Plc.deviceLifterCoordMap.get(new Node(coord.getX() + 1, coord.getY(), coord.getZ())) != null) {
            Coord coord1 = Plc.deviceLifterCoordMap.get(new Node(coord.getX() + 1, coord.getY(), coord.getZ())).getCoord();
            Node node = nodeDBService.getNode(Node.parseGrid(coord1.toString()));
            list.add(node);
        }
        if (Plc.deviceLifterCoordMap.get(new Node(coord.getX() - 1, coord.getY(), coord.getZ())) != null) {
            Coord coord1 = Plc.deviceLifterCoordMap.get(new Node(coord.getX() - 1, coord.getY(), coord.getZ())).getCoord();
            Node node = nodeDBService.getNode(Node.parseGrid(coord1.toString()));
            list.add(node);
        }
        if (Plc.deviceLifterCoordMap.get(new Node(coord.getX(), coord.getY() + 1, coord.getZ())) != null) {
            Coord coord1 = Plc.deviceLifterCoordMap.get(new Node(coord.getX(), coord.getY() + 1, coord.getZ())).getCoord();
            Node node = nodeDBService.getNode(Node.parseGrid(coord1.toString()));
            list.add(node);
        }
        if (Plc.deviceLifterCoordMap.get(new Node(coord.getX(), coord.getY() - 1, coord.getZ())) != null) {
            Coord coord1 = Plc.deviceLifterCoordMap.get(new Node(coord.getX(), coord.getY() - 1, coord.getZ())).getCoord();
            Node node = nodeDBService.getNode(Node.parseGrid(coord1.toString()));
            list.add(node);
        }
        list.add(lifterNode);
        return list;
    }

    private Node findBestNode(Node node) {
        //有3个点时其中有一个是提升机节点两个是提升机接驳点,如果只有
        //两个点代表提升机只有一个可放货的点不做选择/lifterNodes.get(0)为起点、lifterNodes.get(1)为终点
        if (lifterNodes.size() == 3) {
            for (int i = 0; i < lifterNodes.size(); i++) {
                if (node.equals(lifterPathNodeMap.get(LifterPathNode.START_NODE))) {
                    return lifterNodes.get(0);
                } else {
                    return lifterNodes.get(1);
                }
            }
        }
        return node;
    }

    /**
     * 判断改变提升机起点与终点后是否能进行路径规划
     *
     * @param node
     * @return
     */
    private boolean isCanGo(Node node) {
        try {
            getRoute(node, taskDB);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 获取路径规划信息
     *
     * @param node
     * @param taskDB
     * @return
     */
    private Route getRoute(Node node, TaskDB taskDB) {
        if (isStartAndEnd(node)) {
            return routeFactory.createRouteNoLifterNode(taskDB.selectStartNode(), node, false);
        } else {
            return routeFactory.createRouteNoLifterNode(taskDB.selectEndNode(), node, false);
        }
    }

    /**
     * 判断该点是起点改变还是终点改变
     *
     * @param node
     * @return
     */
    private boolean isStartAndEnd(Node node) {
        Node startNode = lifterPathNodeMap.get(LifterPathNode.START_NODE);
        Node endNode = lifterPathNodeMap.get(LifterPathNode.END_NODE);
        if (node.getZ() == startNode.getZ()) {
            return true;
        } else if (node.getZ() == endNode.getZ()) {
            return false;
        }
        return false;
    }


    public enum LifterPathNode {
        START_NODE("起点", 1),
        END_NODE("终点", 2),
        LIFTER_PATH_NODE("提升机点", 3);
        private String desc;
        private Integer code;

        LifterPathNode(String desc, Integer code) {
            this.desc = desc;
            this.code = code;
        }
    }
}
